var lmnpopHistory = {
    changed : true,
    dbFile : null,
    dbConn : null,
    dbSchema: {
        tables: {
            history:"id         INTEGER PRIMARY KEY AUTOINCREMENT, \
                    title       TEXT, \
                    url         TEXT, \
                    loadpage    BOOL, \
                    embedHTML   TEXT, \
                    embedWidth  INTEGER, \
                    embedHeight INTEGER"
        }
    },
    
    open : function() {
        if (!this.dbFile) {
            Components.utils.import("resource://gre/modules/FileUtils.jsm");
            this.dbFile = FileUtils.getFile("ProfD", ["popvideo.sqlite"]);
        }

        var store = Components.classes["@mozilla.org/storage/service;1"].
                        getService(Components.interfaces.mozIStorageService);
        if (!this.dbFile.exists()) {
            this.dbConn = store.openDatabase(this.dbFile);
            for(var name in this.dbSchema.tables)
                this.dbConn.createTable(name, this.dbSchema.tables[name]);
        } else {
            this.dbConn = store.openDatabase(this.dbFile);
        }
    },
    
    close : function(iStorageCompletionCallback) {
        if (this.dbConn) {
            if (typeof this.dbConn.asyncClose !== "undefined")
                this.dbConn.asyncClose(iStorageCompletionCallback);
            else
                this.dbConn.close();
        }
    },
    
    query : function(callback, limit) {
        this.open();
        var statement = this.dbConn.createStatement("SELECT * FROM history ORDER BY id DESC LIMIT " + (limit || 10));
        statement.executeAsync({  
            handleResult: function(aResultSet) {  
                for (let row = aResultSet.getNextRow(); row; row = aResultSet.getNextRow()) {  
                    callback(row);
                }
            },
            handleError: function(aError) {
                //no op
            },
            handleCompletion: function(aReason) {
                //on op
            }
        }); 
        statement.finalize();
        this.close();
        this.changed = false;
    },
    
    insert : function(values, callback) {
        if (this.checkPrivateMode()) {
            return;
        }
        this.open();
        var statement = this.dbConn.createStatement("INSERT INTO history VALUES(:id,:title,:url,:loadpage,:embedHTML,:embedWidth,:embedHeight)");
        for(let p in statement.params){
            statement.params[p] = values[p];
        }
        statement.executeAsync({  
            handleResult: function(aResultSet) {  
                //no op
            },
            handleError: function(aError) {
                //no op
            },
            handleCompletion: function(aReason) {
                if (aReason == 0) {
                    let stmt = lmnpopHistory.dbConn.createStatement("SELECT last_insert_rowid() AS id");
                    if (stmt.executeStep()) {
                        callback(stmt.row.id);
                    }
                    stmt.finalize();
                }
            }
        }); 
        statement.finalize();
        this.close();
        this.changed = true;
    },
    
    update : function(values) {
        if (this.checkPrivateMode()) {
            return;
        }
        this.open();
        var statement = this.dbConn.createStatement("UPDATE history SET title=:title,url=:url,embedHTML=:embedHTML WHERE id=:id");
        for(let p in statement.params){
            statement.params[p] = values[p];
        }
        statement.executeAsync();
        statement.finalize();
        this.close();
        this.changed = true;
    },
    
    del : function(id) {
        if (this.checkPrivateMode()) {
            return;
        }
        this.open();
        var statement = this.dbConn.createStatement("DELETE FROM history WHERE id= :id");
        statement.params.id = id;
        statement.executeAsync();
        statement.finalize();
        this.close();
        this.changed = true;
    },
    
    clear : function() {
        if (this.checkPrivateMode()) {
            return;
        }
        try {
            if (this.dbFile.exists()) {
                this.dbFile.remove(false);
                this.dbConn = null;
            }
        } catch(ex) {
            this.close({  
                complete : function() {  
                    if (lmnpopHistory.dbFile.exists()) {
                        lmnpopHistory.dbFile.remove(false);
                        lmnpopHistory.dbConn = null;
                    }
                }
            });
        }
        this.changed = true;
    },
    
    changeVideoToEmbed: function (lmn) {
        if (lmn.nodeName.toUpperCase() != 'OBJECT') {
            return lmn;
        }

        var embed;
        var ownerDoc = lmn.ownerDocument;
        var children = lmn.childNodes;
        for(var j=0; j<children.length; ++j) {
            if (children[j].nodeName.toUpperCase() == 'EMBED') {
                embed = children[j].cloneNode(true);
                break;
            }
        }

        if (!embed) {
            embed = ownerDoc.createElement('embed');
            for(var i=0; i<children.length; ++i) {
                if (children[i].nodeName.toUpperCase() == 'PARAM') {
                    embed.setAttribute(children[i].getAttribute('name'), children[i].getAttribute('value'));
                }
            }
            embed.setAttribute('type', "application/x-shockwave-flash");
            embed.setAttribute('src', lmn.getAttribute('data'));
        }

        if (embed.id == '') {
            embed.id = (lmn.id == '' ? 'lmnpopPlayer' : lmn.id);
        }
        return embed;
    },
    
    getOuterHTML : function(ele) {
        var a=ele.attributes, str="<"+ele.tagName;
        for(var i=0;i<a.length;i++) 
            if(a[i].specified) 
                str += " " + a[i].name + '="' + a[i].value.replace("&","&amp;").replace("<","&lt;").replace(">","gt") + '"'; 
        if(/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(ele.tagName.toLowerCase())) 
            return str+" />"; 
        return str+">"+ele.innerHTML+"</"+ele.tagName+">";   
    },
    
    checkPrivateMode : function() {
        let pbService;
        let PrivateBrowsingUtils;

        // get the nsIPrivateBrowsingService if it exists
        try {
            pbService = Components.classes["@mozilla.org/privatebrowsing;1"].
                    getService(Components.interfaces.nsIPrivateBrowsingService);

            // a dummy service exists for the moment (Fx20 atleast), but will be removed eventually
            // ie: the service will exist, but it won't do anything and the global private browing
            //     feature is not really active.  See Bug 818800 and Bug 826037
            if (!('privateBrowsingEnabled' in pbService)) {
                pbService = undefined;
            } else if (pbService.privateBrowsingEnabled) {
                this.changed = false;
                return true;
            } else {
                return false;
            }
        } catch(e) { /* Private Browsing Service has been removed (Bug 818800) */ }
        
        
        try {
            PrivateBrowsingUtils = Components.utils.import('resource://gre/modules/PrivateBrowsingUtils.jsm', {}).PrivateBrowsingUtils;
            // checks that global private browsing is implemented
            // if the global private browsing service dne here then it dne at all or it is a dummy
            let isGlobalPBSupported = !!pbService;

            // checks that per-window private browsing is implemented
            // if the global pb service dne but the PrivateBrowsingUtils.jsm exists then assume
            // is enabled.
            let isWindowPBSupported = !isGlobalPBSupported && !!PrivateBrowsingUtils;

            if (isWindowPBSupported) {
                var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
                        getService(Components.interfaces.nsIWindowMediator);
                var mainWindow = wm.getMostRecentWindow("navigator:browser");
                if (PrivateBrowsingUtils.isWindowPrivate(mainWindow)) {
                    this.changed = false;
                    return true;
                }
            }
        }
        catch(e) { /* if this file DNE then an error will be thrown */ }

        return false;
    }
};

